#include "crc.h"

static bool mInitialized=false;
static bool  mIsLittleEndian;
static NxU32 mCRCTable[256];

static  inline NxU32 myhtonl(NxU32 n_ecx)
{
	NxU32 n_eax = n_ecx;           //mov         eax,ecx
	NxU32 n_edx = n_ecx;           //mov         edx,ecx
	n_edx = n_edx << 16;           //shl         edx,10h
	n_eax = n_eax & 0x0FF00;       //and         eax,0FF00h
	n_eax = n_eax | n_edx;         //or          eax,edx
	n_edx = n_ecx;                 // mov         edx,ecx
	n_edx = n_edx & 0x0FF0000;     //and edx,0FF0000h
	n_ecx = n_ecx >> 16;           //shr         ecx,10h
	n_edx = n_edx | n_ecx;         //or          edx,ecx
	n_eax = n_eax << 8;            //shl         eax,8
	n_edx = n_edx >> 8;            //shr         edx,8
	n_eax|=n_edx;                  //  71AB2BE9  or          eax,edx
	return n_eax;
}

static  inline void getRand(NxU32 &current)
{
	current = (current * 214013L + 2531011L) & 0x7fffffff;
};

static  bool inline isBigEndian() { int i = 1; return *((char*)&i)==0; }



static void initCRC(void)
{
    const NxU32 QUOTIENT=0x04c11db7;
    for (NxU32 i = 0; i < 256; i++)
    {
      NxU32 crc = i << 24;
      for (NxU32 j = 0; j < 8; j++)
      {
        if (crc & 0x80000000)
          crc = (crc << 1) ^ QUOTIENT;
        else
          crc = crc << 1;
      }
      mCRCTable[i] = myhtonl(crc);
    }
    mIsLittleEndian = !isBigEndian();
}


NxU32 computeCRC(const NxU8 *data,NxU32 len)
{
	if ( !mInitialized )
	{
		mInitialized = true;
		initCRC();
	}

    NxU32        dlen = (len/4)*4;
    NxU32        result=len;
    NxU32        *p = (NxU32 *)data;
    NxU32        *e = (NxU32 *)(data + dlen);
    NxU32         current = len;


	if ( dlen >= 4 )
	{
		result = ~*p++;
		result = result ^ len;

		const NxU32 *tmp = (const NxU32 *) data;
		current = *tmp & len;

		if ( mIsLittleEndian )
		{
		  while( p<e )
		  {
			getRand(current);
			result = mCRCTable[result & 0xff] ^ result >> 8;
			result = mCRCTable[result & 0xff] ^ result >> 8;
			result = mCRCTable[result & 0xff] ^ result >> 8;
			result = mCRCTable[result & 0xff] ^ result >> 8;
			result ^= *p++;
			result ^= current;
			current &= result; // feed the result back into the random number seed, this forces the random sequence to drift with the input charcter stream.
		  }
		}
		else
		{
		  while( p<e )
		  {
			getRand(current);
			result = mCRCTable[result >> 24] ^ result << 8;
			result = mCRCTable[result >> 24] ^ result << 8;
			result = mCRCTable[result >> 24] ^ result << 8;
			result = mCRCTable[result >> 24] ^ result << 8;
			result ^= *p++;
			result ^= current;
			current &= result; // feed the result back into the random number seed, this forces the random sequence to drift with the input charcter stream.
		  }
	  }


    }
    NxU32 partial = len&3;
    if ( partial )
    {
      for (NxU32 i=0; i<partial; i++)
      {
          getRand(current);
          NxU8 v = data[dlen+i];
          result = mCRCTable[v]^result;
          result^=current;
          current &= result;
      }
    }
    return ~result;
  }


